using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections;
using System.Reflection;

namespace Toolbox.ArrayRenderer
{
    public partial class ArrayFlatList : ListView, IArrayRenderer
    {
        #region Delegate

        public delegate void Parser(ListViewItem c, object o, params object[] parameters);

        private Parser rendering;

        public void SetRenderingFunction(Parser renderingFunction)
        {
            this.rendering = renderingFunction;
        }

        #endregion

        public ArrayFlatList()
            : base()
        {
            this.rendering = Default;
            this.InitializeComponent();
        }

        #region IArrayRenderer Members

        public event ProgressChangedEventHandler ProgressChangedEvent;

        public void ReportProgress(int percentProgress)
        {
            this.BackgroundRenderer.ReportProgress(percentProgress);
        }

        public bool IsRendering
        {
            get { return this.BackgroundRenderer.IsBusy; }
        }

        private bool root = false;

        [Description("Show object as root element")]
        [DefaultValue(false)]
        public bool ShowRootElement
        {
            get { return this.root; }
            set { this.root = value; }
        }

        private bool follow = false;

        [Description("Scan arrays in arrays")]
        public bool FullScan
        {
            get { return this.follow; }
            set { this.follow = value; }
        }

        private ImageList icons;

        [Description("ImageList with type names as key")]
        public ImageList ImageList
        {
            get { return this.icons; }
            set { this.icons = value; }
        }

        public void SetObject(System.Collections.IEnumerable o)
        {
            if (!this.BackgroundRenderer.IsBusy)
            {
                this.OnRendering();
                this.BackgroundRenderer.RunWorkerAsync(o);
            }
        }

        #endregion

        #region Render

        private void BackgroundRenderer_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument is IEnumerable)
            {
                IEnumerable array = this.root ? new object[] { e.Argument } : e.Argument as IEnumerable;

                e.Result = new ListView();
                this.ParseObject(this.rendering, (e.Result as ListView).Items, array, true);

            }
        }

        private void BackgroundRenderer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            this.Items.Clear();

            foreach (ListViewItem i in (e.Result as ListView).Items)
            {
                if (!this.Items.ContainsKey(i.Name))
                {
                    try
                    {
                        i.Remove();
                        this.Items.Add(i);
                    }
                    catch
                    {
                    }
                }
            }


            this.groupTables = new Hashtable[this.Columns.Count];
            for (int column = 0; column < this.Columns.Count; column++)
            {
                this.groupTables[column] = SetGroupsTable(column);
            }

            this.OnRendered();
        }

        private void BackgroundRenderer_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (this.ProgressChangedEvent != null)
            {
                this.ProgressChangedEvent(this, e);
            }
        }

        #endregion

        #region Default Renderer

        private void Default(ListViewItem c, object o, params object[] parameters)
        {
            this.SetIcon(c);

            ParseProperties(c, o);

            if (o is IEnumerable && follow)
            {
                ParseObject(this.Default, c.ListView.Items, o, follow, parameters);
            }
        }

        #endregion

        #region Events

        public event EventHandler Rendering;
        public event EventHandler Rendered;

        protected virtual void OnRendering()
        {
            this.UseWaitCursor = true;

            if (this.Rendering != null)
            {
                this.Rendering(this, new EventArgs());
            }
        }

        protected virtual void OnRendered()
        {
            this.UseWaitCursor = false;

            if (this.Rendered != null)
            {
                this.Rendered(this, new EventArgs());
            }
        }

        #endregion

        #region Helpers

        public void ParseObject(Parser f, ListViewItemCollection c, object o, bool follow, params object[] parameters)
        {
            if (o is IEnumerable && o.GetType().IsArray)
            {
                foreach (object e in o as IEnumerable)
                {
                    string key = e.GetHashCode().ToString();
                    ListViewItem l = c[key];
                    if (l == null)
                    {
                        l = c.Add(e.ToString());
                        l.Name = key;
                    }

                    if (f != null)
                    {
                        f(l, e, follow, parameters);
                    }
                }
            }
        }

        public void ParseProperties(ListViewItem i, object o)
        {
            foreach (ColumnHeader h in this.Columns)
            {
                if (h.DisplayIndex > 0)
                {
                    string key = h.Tag is object ? h.Tag.ToString() : h.Text;

                    ListViewItem.ListViewSubItem to = i.SubItems[key];
                    if (to == null)
                    {
                        to = i.SubItems.Add("");
                    }

                    PropertyInfo pi = o.GetType().GetProperty(key);
                    if (pi is PropertyInfo)
                    {
                        to.Name = pi.Name;

                        if (!pi.PropertyType.IsAbstract && (pi.PropertyType.IsClass || pi.PropertyType.IsPrimitive))
                        {
                            to.Tag = pi.GetValue(o, null);
                            to.Text = to.Tag.ToString();
                        }
                    }

                    MemberInfo mi = o is Type ? (Type) o : o.GetType();
                    if (mi is MemberInfo)
                    {
                        foreach (object a in mi.GetCustomAttributes( true))
                        {
                            if (a is Attribute && a.GetType().ToString().Equals(key))
                            {
                                to.Name = a.GetType().ToString();
                                to.Text = a.ToString();
                                break;
                            }
                        }
                    }
                }
            }


            //foreach (PropertyInfo pi in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.GetProperty))
            //{
            //    if (!pi.PropertyType.IsAbstract && pi.PropertyType.IsClass)
            //    {
            //        ListViewItem.ListViewSubItem to = i.SubItems[pi.Name];
            //        if (to == null)
            //        {
            //            to = i.SubItems.Add(pi.Name);
            //        }

            //        to.Name = pi.Name;
            //        to.Tag = pi.GetValue(o, null);
            //        to.Text = to.Tag.ToString();
            //    }
            //}
        }

        public void SetIcon(ListViewItem i)
        {
            if (this.ImageList is object)
            {
                if (i.Tag is object && this.ImageList.Images.ContainsKey(i.Tag.GetType().FullName))
                {
                    i.ImageKey = i.Tag.GetType().FullName;
                }
                else if (this.ImageList.Images.ContainsKey(i.Text))
                {
                    i.ImageKey = i.Text;
                }
            }
        }

        public void SetGroups(int column)
        {
            // Remove the current groups.
            this.Groups.Clear();

            // Retrieve the hash table corresponding to the column.
            Hashtable groups = (Hashtable)this.groupTables[column];

            // Copy the groups for the column to an array.
            ListViewGroup[] groupsArray = new ListViewGroup[groups.Count];
            groups.Values.CopyTo(groupsArray, 0);

            // Sort the groups and add them to myListView.
            //Array.Sort(groupsArray, new ListViewGroupSorter( this.pluginList.Sorting));
            this.Groups.AddRange(groupsArray);

            // Iterate through the items in myListView, assigning each 
            // one to the appropriate group.
            foreach (ListViewItem item in this.Items)
            {
                // Retrieve the subitem text corresponding to the column.
                string subItemText = item.SubItems[column].Text;

                // For the Title column, use only the first letter.
                if (column == 0)
                {
                    subItemText = subItemText != "" ? subItemText.Substring(0, 1) : "";
                }

                // Assign the item to the matching group.
                item.Group = (ListViewGroup)groups[subItemText];
            }
        }

        private Hashtable[] groupTables;

        private Hashtable SetGroupsTable(int column)
        {
            // Create a Hashtable object.
            Hashtable group = new Hashtable();

            // Iterate through the items in myListView.
            foreach (ListViewItem item in this.Items)
            {

                if (item.SubItems.ContainsKey(this.Columns[column].Text))
                {
                }

                // Retrieve the text value for the column.
                string subItemText = (this.Columns[column].Text.Length > 0 && item.SubItems.ContainsKey(this.Columns[column].Text)) ? item.SubItems[this.Columns[column].Text].Text : item.SubItems[column].Text;

                // Use the initial letter instead if it is the first column.
                if (column == 0)
                {
                    subItemText = subItemText != "" ? subItemText.Substring(0, 1) : "";
                }

                // If the groups table does not already contain a group
                // for the subItemText value, add a new group using the 
                // subItemText value for the group header and Hashtable key.
                if (!group.Contains(subItemText))
                {
                    group.Add(subItemText, new ListViewGroup(subItemText, HorizontalAlignment.Left));
                }
            }

            return group;
        }

        #endregion
    }
}
